Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stats: json optimization of histograms #3280

Merged
merged 17 commits into from
May 10, 2018

Conversation

ramaraochavali
Copy link
Contributor

@ramaraochavali ramaraochavali commented May 3, 2018

Signed-off-by: Rama rama.rao@salesforce.com
Description:
optimizes the json representation of histograms by sending all supported_quantile once and computed_quantlie array multiple times.

Risk Level: Low
Testing: Manual
Docs Changes: NA
Release Notes: NA

Fixes #3102
Fixes #3210

Signed-off-by: Rama <rama.rao@salesforce.com>
@ramaraochavali
Copy link
Contributor Author

@mattklein123 @jmarantz @mrice32
I screwed up the original PR #3184 trying to correct DCO. Sorry.
Here is the new PR.
Addressed all the comments in the previous PR and added docs.
PTAL.

Signed-off-by: Rama <rama.rao@salesforce.com>
Copy link
Member

@mrice32 mrice32 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly looks good. Just a few small comments on the docs.

},
{
"histograms": {
"supported_quantiles": [...],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add real values here, so the user knows what the array will look like (as in, is it an array of doubles or subobjects)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will add.

"name": "example.histogram",
"values": [
{
"interval": null,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above, it would be nice to see an example of a histogram with non-null quantiles values since that will be the common case. But it'd also be nice to keep some sort of null example so the user knows what a no-data hist will look like.

Side note: will this ever print histograms that have no samples in both cumulative and internal since that would mean its used() method would return false?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will make those changes. and regarding used, it currently prints all histograms. And I think it is a good point, it would be good filter the unused ones. I will make that change as well.

Signed-off-by: Rama <rama.rao@salesforce.com>
Signed-off-by: Rama <rama.rao@salesforce.com>
Signed-off-by: Rama <rama.rao@salesforce.com>
mattklein123
mattklein123 previously approved these changes May 4, 2018
Copy link
Member

@mattklein123 mattklein123 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM if it passes @mrice32 @jmarantz muster.

@ramaraochavali
Copy link
Contributor Author

release test is failing with server_fuzz_test unrelated to this.

@mattklein123
Copy link
Member

@ramaraochavali try merging master, @htuch put in a fix that might help.

Signed-off-by: Rama <rama.rao@salesforce.com>
Signed-off-by: Rama <rama.rao@salesforce.com>
if (!shutting_down_) {
ASSERT(!merge_in_progress_);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i just moved this check because, I hit the same issue in CI. Since #3289 fixes it properly we should be good.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be great to get that PR in first to avoid affecting this one :)


{
"histograms": {
"supported_quantiles": [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the indentation in this description looks wrong. This line should be indented one more level than the previous one. Do you think it also makes sense, doc purpose, to put these 9 quantiles each on one line?

"name": "cluster.external_auth_cluster.upstream_cx_length_ms",
"values": [
{
"interval": 0,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for doc legibility purposes, consider this format:

        "values": [
           {"interval": 0, "cumulative": 0},
           {"interval": 0, "cumulative": 0},
           {"interval": 1.0435787, "cumulative": 1.0435787},
           ....

I assume the whitespace output is not actually what the json serializer actually emits, right? It would omit all whitespace?

]
}
]
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the missing indentation earlier forces a few lines of 1-char indent. All this is hard to see because of all the newlines above.

for (Json::ObjectSharedPtr obj_ptr : statsjson->getObjectArray("stats")) {
if (obj_ptr->hasObject("histograms")) {
histogram_count++;
const Json::ObjectSharedPtr& histograms_ptr = obj_ptr->getObject("histograms");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused here: you are looking for 'histograms' plural so I was expecting another loop over all the histograms. Can you explain?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a toplevel "histograms" element that has supported_quantiles array and computed_quantiels, which is again an array of actual histogram computed values in the form of "name" and "values" (interval and cumulative) - the last array is repeated as many times as there are histograms"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still not entirely clear on the cardinality of variouis things from looking at the code. If we actually add a second histogram and make the test expect 2?

Copy link
Contributor Author

@ramaraochavali ramaraochavali May 4, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no. it is still 1. The computed_quantiles array size would be equivalent to the number of histograms.
Cardinality of histograms : 1
Cardinality of supported_quantile array : 1
Cardinality if computed_quantiles array = number of histograms (with each element having histogram name and values array which has interval and cumulative values)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please add a second histogram to this test, so computed_quantiles array has length 2?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not adding any histograms in the test. I just grab whatever the output is from /stats?format=json and validate that it has histograms, it would definitely some (but non predictable). I am not setting up any thing for this test.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, very cool. Let me rephrase my request. Can you make a new test where you add some specific histogram data with more than one histogram, and check the JSON output?

Signed-off-by: Rama <rama.rao@salesforce.com>
]
}
"supported_quantiles": [
0,25,50,75,90,95,99,99.9,100
Copy link
Contributor

@jmarantz jmarantz May 4, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can you put a single space after each comma?

],
"computed_quantiles": [
{
"name": "cluster.external_auth_cluster.upstream_cx_length_ms",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make this 2-space-per-nesting indent consistently; here you have 3.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is two only. I checked by enabling white space. Github is showing like that.


const std::vector<Json::ObjectSharedPtr>& computed_quantiles =
histograms_ptr->getObjectArray("computed_quantiles");
EXPECT_GT(computed_quantiles.size(), 0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we make this EXPECT_EQ something specific here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this count would be equivalent to number of histograms. I do not know exactly how many will come that is why I have added this GT zero condition.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't the number of histograms predictable in this test?

for (Json::ObjectSharedPtr obj_ptr : statsjson->getObjectArray("stats")) {
if (obj_ptr->hasObject("histograms")) {
histogram_count++;
const Json::ObjectSharedPtr& histograms_ptr = obj_ptr->getObject("histograms");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still not entirely clear on the cardinality of variouis things from looking at the code. If we actually add a second histogram and make the test expect 2?

Signed-off-by: Rama <rama.rao@salesforce.com>
],
"computed_quantiles": [
{
"name": "cluster.external_auth_cluster.upstream_cx_length_ms",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have this:

+       {
+          "name": "cluster.external_auth_cluster.upstream_cx_length_ms",

I think it should be this:

+       {
+         "name": "cluster.external_auth_cluster.upstream_cx_length_ms",

if (!shutting_down_) {
ASSERT(!merge_in_progress_);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be great to get that PR in first to avoid affecting this one :)


const std::vector<Json::ObjectSharedPtr>& computed_quantiles =
histograms_ptr->getObjectArray("computed_quantiles");
EXPECT_GT(computed_quantiles.size(), 0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't the number of histograms predictable in this test?

for (Json::ObjectSharedPtr obj_ptr : statsjson->getObjectArray("stats")) {
if (obj_ptr->hasObject("histograms")) {
histogram_count++;
const Json::ObjectSharedPtr& histograms_ptr = obj_ptr->getObject("histograms");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please add a second histogram to this test, so computed_quantiles array has length 2?

Signed-off-by: Rama <rama.rao@salesforce.com>
@ramaraochavali
Copy link
Contributor Author

@jmarantz since that is a private method used only at this place, I thought this test is sufficient. I am not sure if we can add tests for private methods in the current test infrastructure. Will have to check. If you think it adds value, I will do it next week

@jmarantz
Copy link
Contributor

jmarantz commented May 4, 2018

you can structure the test exactly as it is here, if you like, via the admin interface, if that's easier; I just think it's reasonable to control the data. It's also possible to test private methods by friending the test-class and putting a helper there, or adding a TestingPeer class which is friended, if that helps unit-tests.

Copy link
Member

@mrice32 mrice32 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM once @jmarantz's comments are resolved.

Signed-off-by: Rama <rama.rao@salesforce.com>
@ramaraochavali
Copy link
Contributor Author

@jmarantz i will try adding the test next week. I did not get how I can structure the test with admin interface though. How will clear the existing histograms/add the histograms I want in the admin test - i do not have access to stats_store I guess. I have not looked at the code. Are you suggesting some thing different? May not getting your suggestion...

@jmarantz
Copy link
Contributor

jmarantz commented May 6, 2018

RE test for >1 histogram: I would actually prefer you add it as a unit test anyway, which sounds easier. The 'private' issue is easily dealt with by either friending the test-class or a TestingPeer class, either of which can act as in intermediary to access a private method.

Signed-off-by: Rama <rama.rao@salesforce.com>
Signed-off-by: Rama <rama.rao@salesforce.com>
* This is a heap test allocator that works similar to how the shared memory allocator works in
* terms of reference counting, etc.
*/
class TestAllocator : public Stats::RawStatDataAllocator {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jmarantz added a unit test that verifies the json format. PTAL. I had to copy this TestAllocator class as is from thread_local_store_test.cc as I found that it is the easiest way to generate histograms with stores. I do not know if there is any way to make this class across both the files, with out changing much..

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you move the TestAllocator class to test/test_common/utility.h so you don't have duplicated code?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Moved

{"interval": null, "cumulative": 3.0665233},
{"interval": null, "cumulative": 6.046609},
{"interval": null, "cumulative": 229.57333},
{"interval": null, "cumulative": 260}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great now. Much easier to read. Thanks!


std::string actual_json = statsAsJsonHandler(all_stats, store_->histograms());

const std::string expected_json = R"EOF({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reminds me; do we always generate pretty-printed JSON? I think it would be better to deliver it compact in production, though it's nice to have the test be pretty-printed for test.cc legibility.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Currently we do generate pretty-printed JSON. Now I have added a parameter to control that and defaulted it to false and changed the test to print in pretty form. PTAL.

Signed-off-by: Rama <rama.rao@salesforce.com>
Signed-off-by: Rama <rama.rao@salesforce.com>
Signed-off-by: Rama <rama.rao@salesforce.com>
Copy link
Contributor

@jmarantz jmarantz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great, thanks for this! This test adds confidence for me (even though it might not have affected line-coverage).

return;
}

for (auto i = stats_.begin(); i != stats_.end(); i++) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was pre-existing, but you have a map here, can't we just remove it via the name? I don't really care about perf for the test but I think the code would just be:

  if (stats_.erase(std::string(data.name()) == 0) {
    FAIL();
  }

which is fewer lines :)

}
quantile_obj.AddMember("interval", interval_value, allocator);
Value cumulative_value;
if (!std::isnan(histogram->cumulativeStatistics().computedQuantiles()[i])) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably worth commenting here that as we are positionally associating this entry with a quantile, so we need to put in the {null, null} entry to keep other data aligned.

Signed-off-by: Rama <rama.rao@salesforce.com>
@ramaraochavali
Copy link
Contributor Author

@jmarantz addressed the two comments. Should be good now.
@mattklein123 I think we are good here. Can you PTAL ?

@mattklein123 mattklein123 merged commit 64d50ab into envoyproxy:master May 10, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants